perm filename BOARDS[F87,JMC] blob
sn#850853 filedate 1987-12-28 generic text, type T, neo UTF8
;;; -*- Syntax: Common-lisp; Package: PZ; Default-character-style: (:FIX :BOLD :NORMAL) -*-
;;; FLUSH, NEXT and ADD are the three operations allowed on the FIFO-QUEUE.
(defun flush (queue)
(setf (fifo-queue-line queue) nil))
(defun next (queue)
(when (null (fifo-queue-line queue))
(showboard *base-board*)
(error "Loses - the queue is empty"))
(pop (fifo-queue-line queue)))
(defun add (child queue)
(setf (fifo-queue-line queue)
(nconc (fifo-queue-line queue)
(list child))))
;;; COPY-BOARD-POSITION copies one board position onto another. It will tolerate to-board
;;; EQing from-board.
(defun copy-board-position (to-board from-board)
(scl:copy-array-contents (board-position from-board)(board-position to-board))
(setf (board-blank to-board)(board-blank from-board))
(setf (board-completed-chain to-board)(board-completed-chain from-board))
(setf (board-last-complete-row to-board)(board-last-complete-row from-board))
(setf (board-moves to-board)(board-moves from-board))
(setf (board-blank-origin to-board)(board-blank-origin from-board))
to-board)
;;; POSITION-CONTENTS and CURRENT-POSITION are duals. The first, given a place, says what
;;; tile occupies it. The other, given a tile, says what place it occupies.
;;; CURRENT-POSITION is not stored directly and must search.
(defun position-contents (place board)
(aref (board-position board) (1- place)))
(defun current-position (tile board)
(loop for count from 1 to (board-size board)
when (eq (position-contents count board) tile)
return count
finally (error "Never found ~s in ~s~& Contents: ~s~&"
tile (board-name board) (coerce (board-position board) 'list))))
;;; The definitions of both ROW and COLUMN show the curse of zero based indexing.
(defun row (place board)
(1+ (floor (1- place)(board-side board))))
(defun column (place board)
(1+ (mod (1- place) (board-side board))))
;;; Two tiles are contiguous if they are touching edges, or separated only by the blank.
(defun contiguous (tile1 tile2 board)
(let ((p1 (current-position tile1 board))
(p2 (current-position tile2 board)))
(or (and (= (row p1 board)(row p2 board))
(= (abs (- (column p1 board)(column p2 board))) 1))
(and (= (column p1 board)(column p2 board))
(= (abs (- (row p1 board)(row p2 board))) 1))
(and (not (or (eq tile1 :blank)(eq tile2 :blank)))
(contiguous tile1 :blank board)
(contiguous tile2 :blank board)))))
(defun board-size (board)
(array-dimension (board-position board) 0))
(defun leftsquare (place board)
(1+ (* (board-side board) (1- (row place board)))))
;;; If we get through every place in the board without finding a square which is not filled
;;; with its correct number, we've succeeded.
(defun goalp (board)
(let ((count-limit (board-size board)))
(do ((idx 1 (1+ idx)))
((or (= idx count-limit)
(not (equal idx (position-contents idx board))))
(= idx count-limit)))))
(defun stored-successors (movelist board)
(cond ((null movelist) ; NULL MOVELIST iff generating
(when (board-moves board) ; moves from original board.
(error "This movelist ~s doesn't match the board ~s."
movelist (board-name board)))
(aref *adjacency-moves* (1- (board-blank board))))
((eq movelist (board-moves board)) ; When making moves from base-board,
(aref *adjacency-moves* ; don't need to filter retraced steps
(1- (board-blank board))))
(t (remove (or (second movelist) ; Filter retraced steps. When 1 move away
(board-blank board)) ; from original board, have to look at its
(aref *adjacency-moves* ; blank to see where blank camefrom
(1- (car movelist)))))))
;;; BETTER and WORSE, if they succeed, return the heuristic that succeeded. They go through
;;; *worse-measures* or *better-measures*, applying each heuristic until one returns
;;; NonNull.
(defun better (newboard oldboard)
(first (member-if #'(lambda (evaluator)
(funcall evaluator newboard oldboard))
*better-measures*)))
(defun worse (newboard oldboard)
(first (member-if #'(lambda (evaluator)
(funcall evaluator newboard oldboard))
*worse-measures*)))